home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 1 Issue 2 / PDCD-1 - Issue 02.iso / _utilities / utilities / 003 / motorola / Examples / as11 / MCX < prev    next >
Text File  |  1993-06-13  |  74KB  |  1,171 lines

  1.  
  2. *MCX.AS
  3. *******************************************************************************
  4. *                                                                             *
  5. *                            MCX11 REAL-TIME KERNEL                           *
  6. *                               FOR THE MC68HC11                              *
  7. *                                                                             *
  8. *              by                               for support contact           *
  9. *         Tom Barrett                               Mike Wood   M/D OE319     *
  10. *  A.T. Barrett & Associates                       Motorola,Inc               *
  11. *       Houston, Texas                       6501 William Cannon Dr West      *
  12. *                                               Austin, Texas 78735           *
  13. *       (713)728-9688                              (512)891-2717              *
  14. *                                                                             *
  15. *******************************************************************************
  16. *                                N O T I C E                                  *
  17. *******************************************************************************
  18. *                                                                             *
  19. * This product is distributed  without charge  to  users  via  the  MOTOROLA  *
  20. * FREEWARE Bulletin Board. The product is provided "as is" without warranty   *
  21. * of any kind, either expressed or implied, including, but not limited to any *
  22. * warranties of merchantability and fitness for a particular purpose.  All    *
  23. * risks of using this product including the entire costs of any necesary rem- *
  24. * edies are those of the user and MOTOROLA assumes no liability of any kind.  *
  25. *                                                                             *
  26. *******************************************************************************
  27. *******************************************************************************
  28. *                       R E V I S I O N   H I S T O R Y                       
  29. *                                Release 1
  30. *
  31. * Rev #    Date        Modifications/Corrections for new Revision
  32. * -----  ---------     ------------------------------------------------------
  33. *  1.1    5/30/89   1. Change calling sequence of .deque. so that the return
  34. *                      value is in ACCA if entry size is 1 byte.
  35. *
  36. *                   2. Changed calling sequence of .enque. so that if the size
  37. *                      of the entry is 1 byte, it is passed to .enque. in bits
  38. *                      8-15 of IX. If the size of the entry is 2 bytes, IX8-15
  39. *                      contains byte 1 while IX0-7 contains byte 2.
  40. *
  41. *         11/1/89   3. Corrected incorrect jump destination label in .timer.
  42. *                       from .pend to dopend
  43. *
  44. *                   4. Corrected incorrect branch in .send. causing messages to
  45. *                      be put into a receiver's mailbox in improper order.
  46. *
  47. *                   5. Cleared intlvl during initialization.
  48. *
  49. *  1.2    1/12/90   1. Changed the exit logic in .recv. when there is no msg
  50. *                      waiting in the mailbox. This corrects a problem which
  51. *                      would leave the task semaphore of the receiver task in
  52. *                      a WAIT state. Since the .send. ESR does not signal a
  53. *                      semaphore but simply unblocks the receiving task, it is
  54. *                      not necessary to manipulate the task's semaphore. The
  55. *                      original code is deleted and replaced with a simple
  56. *                      set which backs up the PC and exits.
  57. *
  58. *                   2. Moved the code for the backup PC routine and made it an
  59. *                      internal part of the .deque. function
  60. *
  61. *  1.3    3/23/90   1. Disabled interrupts for the endfast routine in isrrtn
  62. *                      which protects this critical path from being interrupted
  63. *                      after intlvl is decremented.  If this were to happen
  64. *                      multiple ISRs could try to restore a task's state.
  65. *
  66. *                   2. Removed a hanging push in .signal. that was not poped 
  67. *                      before returning if the semaphore was not in the wait
  68. *                      state.  This would only be a problem if an ISR were 
  69. *                      returning to the Dispatcher or another ISR.
  70. *
  71. *                   3. Moved the sei and cli in .signal. to tighten up the 
  72. *                      critical code and improve interrupt response time.
  73. *
  74. *                   4. Moved the sei in .wait. to protect the Critical Code
  75. *                      there, and deleted the cli since immrtn is CC also.
  76. *                      This corrects several problems with tasks that wait on 
  77. *                      semaphores from interrupts.
  78. *                      NOTE:  to shorten interrupt latency here you might
  79. *                      move the cli to after setting the wait and then jump
  80. *                      to endtsk, the tradeoff is another stacked interrupt.
  81. *
  82. *                   5. Changed send and receive to use the _RCVWAT status bit
  83. *                      as was origenally intended instead of _WAIT.  This 
  84. *                      Corrects the problem where sending a message to a task
  85. *                      that is waiting on any semaphore causes it to wake up
  86. *                      as if that semaphore had been signaled.
  87. *
  88. *                   6. Added an sei to .deque. to protect some critical code
  89. *                      in .wait. (see 3 above)
  90. *
  91. *                   7. In .enque. moved the loading of IY with queue data to
  92. *                      later in the routine closer to where it is actualy used.
  93. *                      This fixes the problem where the backup routine would
  94. *                      subtract 2 from a word other than the tasks PC which
  95. *                      was stored on its stack.
  96. *
  97. *                   8. Minor things realy, shaved off a clock cycle in dispch's
  98. *                      tight loop, and used the _PEND symbol in .pend.
  99. *
  100. *  1.4    12/7/90   1. Changed cmpb to cmpa in task 5 of TEST.AS. Dequeue was
  101. *                      changed to return the value in the A accumulator in V1.1.
  102. *
  103. *                   2. Fixed a problem with nested interrupt. If you had a
  104. *                      nested interrupt that did not require a context switch
  105. *                      followed by an interrupt that did require a context
  106. *                      switch the stack became corrupted and the system would
  107. *                      crash. Eliminated endfast routine.
  108. *
  109. *                   3. Fixed a problem with purging the timers. Once a timer
  110. *                      was purged the next timer in the link was not lengthed
  111. *                      the amount of time of the purged timer.
  112. *
  113. *                   4. Fixed a problem with cyclic timers in CLKDRIVER.AS.
  114. *
  115. * 1.5      1/24/91  1. Fixed a problem that I introduced in 1.4 in purge of
  116. *                      timer. Messed with y index register and didn't restore.
  117. *******************************************************************************
  118. *******************************************************************************
  119.  
  120. *       OPT    nol           Remove asterisk if you don't want the listing
  121.  
  122. *******************************************************************************
  123. *                                                                             *
  124. *                             MCX-11 VARIABLES                                *
  125. *                        (These Will Reside in RAM)                           *
  126. *                                                                             *
  127. *******************************************************************************
  128.  
  129. ************************ Filled in by Initialization **************************
  130. tickcnt equ    MCXVAR              Tick counter
  131. FREE    equ    tickcnt+1           Address of first free timer block
  132. ACTIVE  equ    FREE+2              Address of first active timer in list
  133. *******************************************************************************
  134.  
  135. curtsk  equ    ACTIVE+2            Current task (i.e. the active task)
  136. curtcb  equ    curtsk+1            Address of current task's TCB
  137. hipri   equ    curtcb+2            Highest priority task ready to run
  138. pritcb  equ    hipri+1             Address of TCB of highest priority task
  139. *                                    ready to run (see hipri)
  140. intlvl  equ    pritcb+2            Depth of nested interrupts:
  141. *                                       = 0 when in a task
  142. *                                       = >0 when interrupts are nested or 
  143. *                                         in the kernel
  144. temp    equ    intlvl+1            Temporary area
  145. width   equ    temp+2              Work area for queue width
  146. depth   equ    width+1             Work area for queue depth
  147. notmt   equ    depth+1             Work area for queue not empty semaphore
  148.  
  149. *******************************************************************************
  150. *                              TASK STATE EQUATES                             *
  151. *******************************************************************************
  152.  
  153. _SUSPND equ    $80                 SUSPENDed status
  154. _WAIT   equ    $40                 WAITing status
  155. _RCVWAT equ    $20                 Waiting status for message RECEIVE
  156. _IDLE   equ    $01                 Task IDLE status
  157.  
  158. *******************************************************************************
  159. *                             MESSAGE EQUATES                                 *
  160. *******************************************************************************
  161.  
  162. MLINK   equ    0                   Message link pointer
  163. MTASK   equ    2                   Message's sending task
  164. MSEMA   equ    3                   Message semaphore
  165. MBODY   equ    4                   Start of message body
  166.  
  167. *******************************************************************************
  168. *                          TIMER BLOCK EQUATES                                *
  169. *******************************************************************************
  170.  
  171. CLINK   equ    0                   Timer block link pointer
  172. CTOCKS  equ    2                   Clock tocks in timer
  173. CRESET  equ    4                   Reset timer
  174. CTASK   equ    6                   Task waiting on timer
  175. CSEMA   equ    7                   Semaphore number
  176.  
  177. *******************************************************************************
  178. *                          QUEUE HEADER EQUATES                               *
  179. *******************************************************************************
  180.  
  181. CURSIZ  equ    0                   Current size of queue (# of entries)
  182. PIX     equ    1                   Put Index
  183. QSEMA   equ    2                   Active semaphore: NOTMT or NOTFUL (=NOTMT+1)
  184.  
  185. *******************************************************************************
  186. *                                TCB LAYOUT                                   *
  187. *******************************************************************************
  188. *
  189. STATE   equ    0                   Byte 0   Task status:
  190. *                                     bit 7: Suspended
  191. *                                     bit 6: Waiting for an event
  192. *                                     bit 5: Receive wait
  193. *                                     bit 4: - Reserved -
  194. *                                     bit 3: - Reserved -
  195. *                                     bit 2: - Reserved -
  196. *                                     bit 1: - Reserved -
  197. *                                     bit 0: Task not in use
  198. ACTSP   equ    1                   Byte 1-2 Active Stack Pointer for task
  199. MSGTHRD equ    3                   Byte 3-4 Message thread pointer
  200.  
  201. *******************************************************************************
  202. *                            STACK CONTEXT EQUATES                            *
  203. *         (These values represent offsets from the Top-of-Stack +1)           *
  204. *******************************************************************************
  205.  
  206. CCR     equ    0                   Condition Code Register
  207. ACCB    equ    1                   Accumulator B
  208. ACCA    equ    2                   Accumulator A
  209. IX      equ    3                   Index Register X
  210. IXH     equ    3                   Index Register X (High Byte)
  211. IXL     equ    4                   Index Register X (Low Byte)
  212. IY      equ    5                   Index Register Y
  213. PC      equ    7                   Program Counter
  214.  
  215. *******************************************************************************
  216. *                         MISCELLANEOUS EQUATES                               *
  217. *******************************************************************************
  218.  
  219. _PEND   equ    1                   PENDing state
  220.  
  221.         PAGE
  222. *******************************************************************************
  223. *                                                                             *
  224. *                               MCX11 TASK DISPATCHER                             *
  225. *                                                                             *
  226. *******************************************************************************
  227. *                                                                             *
  228. * This is the code that looks for the highest priority task that is in a RUN  *
  229. * state. The tsk's state is contained in the Task Control Block (TCB). The    *
  230. * job of the Dispatcher is to find the task having the highest priority and   *
  231. * is ready to take control, i.e., run. This is a tight loop because every     *
  232. * time a task gives up control, except for a pre-emption, the Dispatcher is   *
  233. * called to find the next task to run.                                        *
  234. *                                                                             *
  235. *******************************************************************************
  236.  
  237. dispch  clra                        Use acc A for the task number
  238.         ldx    #STATLS-TCBLEN       Set up base address of TCB list
  239. next    clr    curtsk               Set current task # to 0
  240.         lds    #SYSTACK             Load the system stack pointer address
  241.         ldab   #$7F                 Load constant 127 for presetting
  242.         stab   hipri                  the task # of the highest priority task
  243. *                                     - 1 is the highest priority
  244. *                                     - 127 is the lowest priority
  245.         ldab   #TCBLEN              Load a constant for TCB length attribute
  246.         cli                         Enable interrupts
  247. testat  inca                        Bump the task number by 1
  248.         abx                         Get address of next TCB
  249. *** (V1.3 #8) ***
  250.         tst    STATE,x              See if all bits in task's status are 0
  251.         bne    testat               If not, go check the next task.
  252. switch  sei                         Task is ready to run. disable interrupts
  253.         staa   hipri                Save task number as highest priority
  254.         stx    pritcb               Save TCB address as that of hipri task
  255. setcur  staa   curtsk                 also save acc A as current task number
  256.         stx    curtcb               Save X-reg as address of curtsk's TCB
  257. rtncur  lds    ACTSP,x              Load task's stack pointer from TCB
  258. intrtn  rti                         Go to task via an interrupt return. The
  259. *                                     task's context is on the task's stack.
  260. *                                     This treats the context switch as if it
  261. *                                     is an interrupt (it is likely to be).
  262.  
  263. *******************************************************************************
  264. *                                                                             *
  265. *                       MCX11 COMMON INTERRUPT SERVICE RETURN                     *
  266. *                                                                             *
  267. *******************************************************************************
  268. *                                                                             *
  269. * This is the code that ALL interrupt service routines (ISR) MUST return thru *
  270. * to complete the action of the interrupt. The ISR must have saved the MPU    *
  271. * context, incremented the nested interrupt level (intlvl), and enabled other *
  272. * interrupts before actually servicing the interrupt. Upon completion,  the   *
  273. * ISR, should branch to this common interrupt return routine. The B accumu-   *
  274. * lator  should  contain a semaphore  number or a zero.  The  former  value   *
  275. * indicates that an event has occurred that requires action at the task level *
  276. * and the latter indicates thee is no further action required.                *
  277. *                                                                             *
  278. * When ACCB contains a semaphore number, the semaphore is SIGNALed to inform  *
  279. * any task associated with the event that it has occurred. A context switch   *
  280. * will occur if the task in a WAIT state for that event is then ready to run  *
  281. * and is of higher priority than the interrupted,i.e. current, task, and the  *
  282. * level of nested interrupts is 1. A context switch will not occur if ALL of  *
  283. * these conditions are not true. If the B accumulator contains a zero upon    *
  284. * return from the ISR, there is an immediate return to the interrupted task   *
  285. * if the level of nested interrupts is 1 or a return to the executive or to   *
  286. * an interrupted ISR if the level of nested interrupts is >1.                 *
  287. *                                                                             *
  288. *******************************************************************************
  289. *
  290. isrrtn  tstb                        Test ACCB for content
  291.         bne    signal2              If ACCB != 0, go signal the event. 
  292. *                                     If ACCB = 0, return to point of interrupt
  293. *
  294. *** (V1.4 #2) ***
  295.         bra    endtsk               Eliminated endfast routine.              |
  296.  
  297. .signal tab                         *** PUT IN COMMENT BLOCK ***
  298. signal2
  299.         ldx    #FLGTBL-1            Got a semaphore number. Load address-1 of
  300.         abx                           semaphore table and add semaphore # to it
  301.         clrb                        Clear ACCB so that we will have a zero
  302.         sei                         Turn off interrupts  *** (V1.3 #2) ***
  303.         ldaa   0,x                  Get the content of the semaphore
  304.         bpl    nowait               Branch if semaphore not in WAIT state
  305.         incb                        If WAITing, set it to PENDing
  306. nowait  stab   0,x                  If PENDing or DONE, set to DONE
  307.         tab                         Look at original content of semaphore
  308. *** (V1.4 #2) ***
  309.         bpl    immrtn               Return immediately if semaphore not WAITing|
  310.         cli                         End of critical code  *** (V1.3 #2) ***
  311.         negb                        If WAITing, 2's complement = task # WAITing
  312. *                                     for the event.
  313. *** (V1.3 #3) ***
  314.         ldaa   #-_WAIT-1            Store the 1's complement of the WAIT
  315. clrstat pshb                        Save the task number.
  316.         psha                        Then save the mask again.
  317.         ldaa   #TCBLEN              Use the task number to compute the address
  318.         mul                           of its TCB
  319.         addd   #STATLS-TCBLEN
  320.         xgdx                        Put the TCB address in IX register
  321.         pula                        Get the mask of bits to clear.
  322. clrbits sei
  323.         anda   0,x                  Clear the byte as given by the mask.
  324.         staa   0,x                  Save the updated TCB status byte.
  325.         pula                        Get the task number again.
  326.         bne    immrtn               Branch to immediate exit if task's status
  327. *                                     indicates it is not ready to run (!=0).
  328.         cmpa   hipri                If it is ready to run, is it of higher
  329. *                                     priority than the current highest 
  330. *                                     priority task which is ready to run?
  331.         bge    immrtn               Branch if task priority < hipri.         
  332.         sta    hipri                If so, then make it the new hipri task.
  333.         stx    pritcb               Save its TCB address too.
  334.  
  335. endtsk  sei                         Disable interrupts
  336. immrtn  dec    intlvl               Decrement the interrupt level.
  337.         bne    intrtn               If it is >0, then the interrupt occurred
  338. *                                     while we were doing system operations.
  339.         ldaa   curtsk               See if the current task is 0.
  340.         bne    notdisp              If not, branch.
  341.         tsx                         If so, the MCX11 Dispatcher was interrupted
  342. *                                     See what task was being checked when the
  343. *                                     interrupt occurred. The context of the
  344. *                                     Dispatcher is found on the system stack.
  345.         ldaa   ACCA,x               Get the content of ACCA from the stack as
  346. *                                     it holds the value of the "current task"
  347. *                                     being used by the Dispatcher.
  348.         cmpa   hipri                Compare it to hipri task number.
  349.         ble    intrtn               The "current task" is of higher priority.
  350. notdisp ldx    pritcb               Get the hipri task's TCB address
  351.         ldaa   hipri                and set up the hipri task number.
  352.         brclr  STATE,x $FF setcur   If task is ready to run, go to it
  353.         bra    next                 Otherwise, look for next lower priority
  354. *                                     task that is ready to run.
  355.  
  356. *******************************************************************************
  357. *                                                                             *
  358. *                    MCX11 EXECUTIVE SERVICES DISPATCHER                      *
  359. *                                                                             *
  360. *******************************************************************************
  361. *                                                                             *
  362. * The Executive Services Dispatcher is the heart of MCX11. This is the point  *
  363. * to which all Executive Service Requests (ESR) are vectored. The ESR Dis-    *
  364. * patcher takes the function code of the ESR requested by the task and uses   *
  365. * it as an index into a jump table to the various ESR functions. This code is *
  366. * entered by a SWI instruction so it must be treated as a special kind of     *
  367. * interrupt. The nested interrupt level is always =0 upon entry because the   *
  368. * ESR is coming from a task. The ESR function is always found at the byte     *
  369. * immediately following the SWI.                                              *
  370. *                                                                             *
  371. *******************************************************************************
  372.  
  373. mcxsrv  inc    intlvl               Increment the interrupt nest level
  374.         ldx    curtcb
  375.         sts    ACTSP,x              Save SP in TCB of current task
  376.         tsy                         Save Top-of-Stack (TOS) in IY. This will
  377. *                                     actually point to the CCR byte
  378.         lds    #SYSTACK             Load SP with address of system stack
  379.         cli                         Now interrupts are turned on
  380.         ldx    PC,y                 Set up pointer to the ESR function
  381.         ldab   0,x                  Get the ESR function code
  382.         inx                         Bump the return address by 1
  383.         stx    PC,y                 Save the return address
  384.         aslb                        Multiply the ESR function code by 2
  385.         ldx    #jtable              Load address of the ESR jump table
  386.         abx                         Add function code*2 to base address of
  387. *                                     jump table to get ESR vector
  388.         ldx    0,x                  Load the ESR vector into IX
  389.         ldd    ACCB,y               Load up ACCA and ACCB with the arguments
  390. *                                     as passed by the calling task but
  391. *                                     switch ACCA and ACCB contents.
  392.  
  393. *******************************************************************************
  394. *                                                                             *
  395. * At this point the processor context is as follows:                          *
  396. *                                                                             *
  397. *              CCR       conditions wrt contents of ACCB & ACCA (don't use)   *
  398. *              ACCA      ACCB as passed from calling task (ESR dependent)     *
  399. *              ACCB      ACCA as passed from calling task (ESR dependent)     *
  400. *              IX        Address of the ESR function                          *
  401. *              IY        Address of task's Top-of-Stack (CCR byte)            *
  402. *              SP        Base of System Stack                                 *
  403. *                                                                             *
  404. * In addition, the nested interrupt level is = 1, curtsk  and hipri are set   *
  405. * to the number of the calling task, and curtcb and hipri both contain the    *
  406. * address of the current task's TCB.                                          *
  407. *                                                                             *
  408. *******************************************************************************
  409.  
  410.         jmp    0,x                  Go to the ESR function
  411.  
  412. *******************************************************************************
  413. *                                                                             *
  414. *                              ESR JUMP TABLE                                 *
  415. *                                                                             *
  416. *******************************************************************************
  417.  
  418. jtable  FDB    endtsk               0  = Immediate return (NOP)
  419.         FDB    .wait                1  = Wait for an event
  420.         FDB    .signal              2  = Signal a semaphore
  421.         FDB    .pend                3  = Set a semaphore to PENDing state
  422.         FDB    .send                4  = Send a task a message without waiting
  423.         FDB    .sendw               5  = Send a task a message with wait
  424.         FDB    .recv                6  = Receive a message
  425.         FDB    .deque               7  = Dequeue an entry from a FIFO queue
  426.         FDB    .enque               8  = Enqueue an entry into a FIFO queue
  427.         FDB    .resume              9  = Resume a task in suspension
  428.         FDB    .suspnd              10 = Suspend a task's operation
  429.         FDB    .termn8              11 = Terminate a task's operaton
  430.         FDB    .xeqt                12 = Execute a task
  431.         FDB    .delay               13 = Delay a task for a period of time
  432.         FDB    .timer               14 = Set up a timer
  433.         FDB    .purge               15 = Remove a given timer from active state
  434.  
  435.  
  436. * ESR1
  437. *******************************************************************************
  438. *                                                                             *
  439. * WAIT for an event to occur. This ESR is used to place the calling task into *
  440. * a WAIT state where it will remain until the event happens. If the event has *
  441. * already occurred, the wait does not happen and control is immediately re-   *
  442. * turned to the calling task. WAIT is always associated with a semaphore. The *
  443. * semaphore can be explicitly stated or it can be implicit, i.e. the task's   *
  444. * semaphore. Regardless of how the semaphore is defined in the ESR, the sema- *
  445. * phore must be in the PENDing state if the WAIT is to occur. The process     *
  446. * handling the signaling of the event when it occurs must have prior know-    *
  447. * ledge that the event uses a particular semaphore. Only in this way can the  *
  448. * waiting task and the signalling task be connected.                          *
  449. *                                                                             *
  450. * calling sequence:                                                           *
  451. *                                                                             *
  452. *                   ACCB = semaphore number                                   *
  453. *                   swi                                                       *
  454. *                    FCB   .wait.                                                   *
  455. *                   return: all registers unchanged                           *
  456. *                                                                             *
  457. *******************************************************************************
  458.  
  459. .wait   tab                        Test the semaphore number
  460.         bne    wait2               If it is defined, go set up the wait
  461.         ldab   curtsk              Otherwise use the task's semaphore
  462.         addb   #NNAMSEM
  463. wait2   ldx    #FLGTBL-1
  464.         abx                        Compute address of the semaphore
  465.         sei                        Start of critical code  *** (V1.3 #4) ***
  466.         tst    0,x                 Test the semaphore contents
  467.         bmi    waitt               Branch if already in the WAIT state
  468. *                                    This is a safety net, if you should unin-
  469. *                                    tentionally have more than one task wait-
  470. *                                    ing on a semaphonre at a time all but the
  471. *                                    first will not wake up unless they are re-
  472. *                                    exicuted.
  473.         beq    pendset             If DONE, go reset to PENDing and return
  474. force   ldaa   curtsk              If state of semaphore is PENDing, the
  475.         nega                         event has not yet occurred so
  476.         staa   0,x                 Set the semaphore to WAIT on current task.
  477. waitt   ldx    curtcb              Get current task's TCB address
  478.         bset   STATE,x _WAIT       Set the WAIT state in task's status byte.
  479. *** (V1.3 #4) ***
  480.         jmp    immrtn              Critical code ends at actual return to task
  481.  
  482. *******************************************************************************
  483. *                                                                             *
  484. * Force a semaphore to a PENDing state. This function should be used to make  *
  485. * sure that a semaphore is in the correct state if there is some doubt. A     *
  486. * semaphore must be in a PENDing state before it can transition to a WAITing  *
  487. * state. All semaphores should be initialized to a PENDing state at the time  *
  488. * of system initialization. After that, they are generally maintained by the  *
  489. * .wait and .signal functions. However, it may become necessary at some time  *
  490. * to set a given semaphore PENDing to insure that the event is recognized as  *
  491. * having yet occurred.                                                        *
  492. *                                                                             *
  493. * calling sequence:                                                           *
  494. *                                                                             *
  495. *                   ACCB = semaphore number                                   *
  496. *                   swi                                                       *
  497. *                    FCB   .pend.                                                   *
  498. *                   return: all registers unchanged                           *
  499. *                                                                             *
  500. *******************************************************************************
  501.  
  502. .pend   tab                        Put semaphore number in ACCB
  503.         bne    dopend              Branch if semaphore is defined.
  504.         ldab   curtsk              Else use task semaphore.
  505.         addb   #NNAMSEM
  506. dopend  ldx    #FLGTBL-1           Compute the address of the given semaphore.
  507.         abx
  508. pendset ldaa   #_PEND              Set the semaphore to pending
  509.         sei
  510.         staa   0,x
  511.         jmp    immrtn              All done.
  512.  
  513. *******************************************************************************
  514. *                                                                             *
  515. * Send a message and wait for reply. This function is the same as the .send.  *
  516. * function except that it puts the sender into a Wait state until the task    *
  517. * receiving the message signals the semaphore to indicate it is finished.     *
  518. *                                                                             *
  519. * calling sequence:                                                           *
  520. *                                                                             *
  521. *                   ACCA = Task number of the receiver                        *
  522. *                   ACCB = Semaphore number                                   *
  523. *                   IX = Message address                                      *
  524. *                   swi                                                       *
  525. *                    FCB   .sendw.                                                  *
  526. *                   return: Registers are unchanged                           *
  527. *                           ACCB will = Current task # if the semaphore       *
  528. *                             number was = 0 at the time of the ESR           *
  529. *                                                                             *
  530. *******************************************************************************
  531.  
  532. .sendw  clrb                       Set switch = 0 for .sendw.
  533.  
  534. *******************************************************************************
  535. *                                                                             *
  536. * Send a message. This function sends a message but does not wait for a reply *
  537. * to be returned from the receiver. There is a semaphore associated with the  *
  538. * message but it is set to a PENDing state. The Sending task may perform a    *
  539. * .wait. on that semaphore at a later time if necessary. Neither .send. nor   *
  540. * .sendw. moves data from the sender to the receiver. Instead, only the       *
  541. * pointer to the message is moved. Each task has a threaded list of messages  *
  542. * which it can receive. The threaded list entry is the pointer to the message.*
  543. * The message pointer is inserted into the threaded list in the order of the  *
  544. * sending task's priority.                                                    *
  545. *                                                                             *
  546. * calling sequence:                                                           *
  547. *                                                                             *
  548. *                   ACCA = Task number of the receiver                        *
  549. *                   ACCB = Semaphore number                                   *
  550. *                   IX = Message address                                      *
  551. *                   swi                                                       *
  552. *                    FCB   .send.                                                   *
  553. *                   return: Registers are unchanged                           *
  554. *                           ACCB will = Task semaphore # if ACCB was = 0      *
  555. *                                       at the time of the ESR.               *
  556. *                                                                             *
  557. *******************************************************************************
  558.  
  559. .send   pshb                       Save the switch. Switch > 0 for .send
  560.         ldab   ACCB,y              Get the semaphore number for the message.
  561.         bne    havsema             Branch if semaphore # > 0.
  562.         ldab   curtsk              If semaphore # = 0, use task semaphore.
  563.         addb   #NNAMSEM
  564.         stab   ACCB,y
  565. havsema ldx    #FLGTBL-1
  566.         abx                        Compute address of the semaphore.
  567.         pulb                       Get the send switch.
  568.         tstb                       Test switch.
  569.         bne    notw                Branch if switch set for no waiting.
  570.         ldaa   curtsk              
  571.         nega                       If switch set for wait, set -task #.
  572. notw    staa   0,x                 Set the semaphore to proper value.
  573.         ldx    curtcb              Get address of current task's TCB.
  574.         tstb                       Test the switch again.
  575.         bne    waitnot
  576.         bset   STATE,x _WAIT       Set the sender's status to WAITing.
  577. waitnot ldaa   ACCA,y              Get the receiving task #.
  578.         psha                       Save it for later use, too.
  579.         ldab   #TCBLEN
  580.         mul                        Compute TCB address of receiving task.
  581.         addd   #STATLS-TCBLEN
  582.         xgdx
  583.         pshx                       Save TCB address for later use.
  584.         ldab   #MSGTHRD
  585.         abx
  586.         ldaa   ACCB,y              Get the semaphore number
  587.         ldy    IX,y                Load IY with message address.
  588.         staa   MSEMA,y             Set up semaphore # in message header.
  589.         ldaa   curtsk
  590.         staa   MTASK,y             Set up current task as sender.
  591.         pshy                       Save address of the message header.
  592. nsrtlp  ldy    MLINK,x             Look at the receiver's next message address.
  593.         beq    insert              If pointer = 0, End-of=List. Go insert here.
  594.         cmpa   MTASK,y             If there is a message on the thread, look at
  595. *                                    the task number of its sender and compare
  596. *                                    it to the sender's task number for this
  597. *                                    new message.
  598. *
  599.         blo    insert              If the new message's sender has a priority |
  600. *                                    higher than that of the threaded message,|
  601. *                                    go insert the new message pointer in front
  602. *                                    of the one on the list. (V1.1 #4)        |
  603.         ldx    MLINK,x             Otherwise, walk the thread.
  604.         bra    nsrtlp              Keep looking for a place to insert new msg.
  605. insert  xgdy                       Put address of next nessage in ACCD.
  606.         puly                       Get address of new message's header.
  607.         std    MLINK,y             Link new message into threrad.
  608.         xgdy
  609.         std    MLINK,x
  610.         pulx                       Get address of TCB of receiver again. 
  611. ***(V1.3 #5)***
  612.         ldaa   #-_RCVWAT-1
  613.         jmp    clrbits             Go clear the receiver wait, if any.
  614.  
  615. *******************************************************************************
  616. *                                                                             *
  617. * Receive a message. This function is used by a receiving task to get the     *
  618. * next message from the task's message thread. Normally, the next message is  *
  619. * pointed to by the content of the message thread pointer in the task's TCB.  *
  620. * However, it is also possible to get the next message sent from a given task *
  621. * to the receiver task. By this technique, the receiver may effectively ig-   *
  622. * nore other senders and reserve all activity for the one sending task. This  *
  623. * is useful in designing server tasks which need to insure the dedicated use  *
  624. * of a given resource.                                                        *
  625. *                                                                             *
  626. * calling sequence:                                                           *
  627. *                                                                             *
  628. *                   ACCA = task number (Otherwise this must be 0)             *
  629. *                   swi                                                       *
  630. *                    FCB   .recv.                                                   *
  631. *                   return: IX contains the address of the received message   *
  632. *                           All other registers unchanged                     *
  633. *                                                                             *
  634. *******************************************************************************
  635.  
  636. .recv   ldx    curtcb              Set up to see if there is a special task
  637.         tstb                       ACCB holds the task number or 0.
  638.         bne    special             If ACCB != 0, it contains the task number.
  639.         ldd    MSGTHRD,x           If not given task #, get next message.
  640.         beq    waitrcv             See if message thread is null. (=0)
  641.         std    IX,y                It's not. We have a message. Save address.
  642.         xgdy
  643.         ldd    0,y                 Then unlink the message from the thread.
  644.         std    MSGTHRD,x
  645.         jmp    endtsk              Wrap it up.
  646. *** (V1.3 #5) ***
  647. waitrcv bset   STATE,x _RCVWAT       If no message available, set up a RCVWAT
  648. *** (V1.2 #1) *** 
  649.         ldd    PC,y                Then back up the PC in the stacked         |
  650.         subd   #2                   context by 2 bytes                        |
  651.         std    PC,y                                                           |
  652.         jmp    endtsk              Go to wrap up routine                      |
  653.  
  654. special pshy                       Save address of the task's stack context.
  655.         ldab   #MSGTHRD            ACCB still contains the task number.
  656.         abx                        Adjust IX to point to message thread pntr.
  657.         ldab   ACCA,y              Get the task number again.
  658. rcvloop ldy    0,x                 Get the address of the next message header.
  659.         beq    waitrcv             If no message available, go wait for one.
  660.         cmpb   MTASK,y             Compare the sender's task number to special
  661.         beq    rcvgo                 task number. If a match, go to it.
  662.         blt    waitrcv               If special task number < sender's, wait.
  663.         ldx    0,x                 If special task number > special, set up IX
  664.         bra    rcvloop               to point to current message header and
  665. *                                    then go check next message.
  666. rcvgo   xgdy                       We have a message. Put its address in ACCD.
  667.         puly                       Then set up IY to point to the stack context
  668.         std    IX,y                Store the message address in the IX register
  669. *                                    of the receiving task's stack context.
  670.         xgdy                       Then put the pointer back into IY.
  671.         ldd    0,y                 Unlink the message from the thread.
  672.         std    0,x
  673.         jmp    endtsk              Go wrap it up.
  674.  
  675.  
  676. *******************************************************************************
  677. *                                                                             *
  678. * Dequeue an element from a FIFO queue. This function is used to remove an    *
  679. * element from a FIFO (First in- First out) queue. The queue is divided into  *
  680. * two parts, the queue header and the queue body. The queue header contains   *
  681. * all of the information about the state of the queue body. An attempt to re- *
  682. * move an entry from an empty queue will place the caller into a wait state.  *
  683. * When the queue has something put into it, the waiting task will be resumed. *
  684. * Queue entries can be of any size but each queue handles only one size of    *
  685. * entry.                                                                      *
  686. *                                                                             *
  687. * calling sequence:                                                           *
  688. *                                                                             *
  689. *                   ACCA = Queue number                                       *
  690. *                   IX =   Destination address of dequeued entry of           *
  691. *                            size > 2 bytes. Otherwise ignored.               *
  692. *                   swi                                                       *
  693. *                    FCB   .deque.                                                  *
  694. *                   return: For an entry size of 1 byte:                      *
  695. *                            ACCA = dequeued entry                            *
  696. *                           For an entry size of 2 bytes:                     *
  697. *                            ACCA = byte 1 of entry                           *
  698. *                            ACCB = byte 2 of entry                           *
  699. *                           Other registers unchanged                         *
  700. *                                                                             *
  701. *******************************************************************************
  702.  
  703. .deque  bsr    qsetup              Set up the queue header information
  704. *                                    The queue depth and width are moved to
  705. *                                    RAM work area as is the Not Empty sema-
  706. *                                    phore. The address of the queue body is
  707. *                                    in RAM at location "temp".
  708.         ldab   CURSIZ,x            Check current size of queue to see if empty.
  709.         beq    qempty              Branch if queue is empty.
  710.         pshx                       Save the queue header address.
  711.         ldaa   PIX,x               Not empty. Get the Put Index and subtract
  712.         sba                          the Current Size from it.
  713.         bhs    nowrap              Check for wrap around.
  714.         adda   depth               If so, add Depth of queue.
  715. nowrap  ldab   width               ACCA = Get Index. Load ACCB with Width of
  716.         pshb                         an entry. Also save the width for later.
  717.         dec    CURSIZ,x            Decrement the Current Size of the queue by 1
  718.         mul                        Now multiply the Width times the Get Index
  719.         addd   temp                  and add the base address of the queue body
  720.         xgdx                         to get address of the entry to dequeue.
  721.         pula                       Get the Width again
  722.         cmpa   #2                  Is it > 2 bytes?
  723.         bgt    dqgt2               Branch if width > 2 bytes.
  724.         bge    dq2                 See if Width = 2 bytes and branch if so.
  725.         ldab   0,x                 Width = 1 byte. Get it.
  726. **(V1.1 #1)**
  727.         stab   ACCA,y              Store it in ACCA of the stack context.     |
  728.         bra    dqend               That's it.
  729. dq2     ldab   0,x                 For Width = 2 bytes, get the 1st byte
  730.         stab   ACCA,y                and store it in ACCA of stack context.
  731.         ldab   1,x                 Then get the 2nd byte and store in in ACCB
  732.         stab   ACCB,y                of the stack context.
  733.         bra    dqend               And that's that for size = 2.
  734. dqgt2   ldy    IX,y                For Width > 2 bytes, get the destination
  735. *                                    address from IX of the stack context.
  736. dqloop  ldab   0,x                 Get next byte from queue body.
  737.         stab   0,y                 Move it to next byte of the destination.
  738.         inx
  739.         iny
  740.         deca                       Decrement the Width count.
  741.         bne    dqloop              Loop until count is = 0.
  742. dqend   pulx                       When done, get the queue header address.
  743.         ldab   QSEMA,x             Get the Queue NOT FULL semaphore #
  744.         beq    nopost              See if anyone is waiting for NOT FULL.
  745. postem  clra                       There is. Go signal the semaphore.
  746.         staa   QSEMA,x             But first, set the active semaphore # = 0
  747.         jmp    signal2
  748. qempty  addb   notmt               Queue is empty. Set up a wait state for this
  749.         stab   QSEMA,x               task on the Queue NOT EMPTY semaphore
  750. ** (V1.2 #2) **
  751. backup  pshb                       Save the semaphore number                  |
  752.         ldd    PC,y                Here we have to back up the PC by 2 bytes  |
  753.         subd   #2                    so that when the task restarts, it will  |
  754. *                                    be at the SWI used to call MCX11.        |
  755.         std    PC,y                Save the decremented PC                    |
  756.         pulb                       Get the semaphore number.                  |
  757.         ldx    #FLGTBL-1                                                      |
  758.         abx                        Calculate address of semaphore.            |
  759. *** (V1.3 #6) ***
  760.         sei                        Prepare to enter critical code
  761.         jmp    force               Go set the wait state and clean up.        |
  762.  
  763. qsetup  clra
  764.         aslb                       Multiply the queue number by the length
  765.         pshb                       Save queue number * 2.
  766.         addb   ACCA,y                of the queue initialization data block.
  767.         aslb
  768.         addd   #QUEDATA-QUEDATL    Then add the address of the queue init data
  769.         xgdx
  770.         pulb                       Get queue number * 2 and add the number of
  771.         addb   #NNAMSEM+NTASKS-1     named semaphores + the number of tasks
  772.         stab   notmt                 to get the NOT EMPTY semaphore #.
  773.         ldd    WIDTH,x             Get the width and depth of the queue
  774.         std    width               Store them in the work area of RAM.
  775.         ldd    QADDR,x             Get the queue body address
  776.         std    temp                Store it in temp word in RAM
  777.         ldx    QHADR,x             Get the Queue Header address
  778.         rts                        Return
  779.  
  780. *******************************************************************************
  781. *                                                                             *
  782. * Enqueue an element into a FIFO queue. This function is used to insert an    *
  783. * element into a FIFO (First in- First out) queue. The queue is divided into  *
  784. * two parts, the queue header and the queue body. The queue header contains   *
  785. * all of the information about the state of the queue body. An attempt to in- *
  786. * sert an entry into a full queue  will place  the caller into a wait state.  *
  787. * When the queue has something taken out of it, the waiting task will be      *
  788. * resumed. Queue entries can be of any size but each queue handles only one   *
  789. * size of entry.                                                              *
  790. *                                                                             *
  791. * calling sequence:                                                           *
  792. *                                                                             *
  793. *                   ACCA = Queue number                                       *
  794. *                   IX =   Source address of entry to be enqueued if          *
  795. *                            size > 2 bytes.                                  *
  796. *                          Or, IX8-15 = byte to be enqueued if size = 1       *
  797. *                          Or, IX8-15 = byte 1 and IX0-7 = byte 2 if size = 2 *
  798. *                   swi                                                       *
  799. *                    FCB   .enque.                                            *
  800. *                   return: Registers are unchanged                           *
  801. *                                                                             *
  802. *******************************************************************************
  803.  
  804. .enque  bsr    qsetup              Set up the queue header information
  805.         ldaa   depth               Compare DEPTH of queue to its Current Size
  806.         cmpa   CURSIZ,x              to see if it is full.
  807.         beq    full                Branch if queue is full.
  808.         pshx                       Save address of the queue header.
  809.         cmpa   PIX,x               Compare the depth to the Put Index to see
  810.         bgt    wrapnot               if there is an address wrap around.
  811.         clra
  812.         staa   PIX,x               If a wrap around, reset Put Index to 0.
  813. wrapnot ldaa   width               Then use the width and the Put index to
  814.         psha                         compute the destination address in the 
  815.         ldab   PIX,x                 queue body.
  816.         mul
  817.         addd   temp   
  818.         inc    PIX,x               Bump the Put Index
  819.         inc    CURSIZ,x            Bump the Current Size
  820. *** (V1.3 #7) ***
  821.         ldy    IX,y                Get data to be enqueued or pointer to it.
  822.         xgdx                       Move destination address into IX.
  823.         pula                       Get the Width again.
  824.         cmpa   #2                  See if Width > 2 bytes.
  825.         bgt    nqloop              Branch if Width > 2 bytes
  826.         xgdy                       If not, the data is in IY. Put it in ACCD.
  827.         beq    nq2                 Branch if Width = 2 bytes.
  828. ** (V1.1 #2) **
  829.         staa   0,x                 Width is 1 byte. Store it from IX8-15.
  830.         bra    nqend               Then go to wrap up routine.
  831. nq2     std    0,x                 For Width of 2 bytes, store from IX0-15.
  832.         bra    nqend
  833. nqloop  ldab   0,y                 For Width > 2 bytes, move a byte from the
  834.         stab   0,x                   source address to the destination address.
  835.         inx
  836.         iny
  837.         deca                       Decrement the counter in ACCA.
  838.         bne   nqloop               Loop until done.
  839. nqend   pulx                       When all done, get the address of the queue
  840.         ldab  QSEMA,x                header, and test for a waiter on queue
  841. *                                    Not Empty.
  842.         bne   postem               If there is a waiting task, go resume it.
  843. nopost  jmp   endtsk               If not, just go back to caller.
  844. full    ldab  #1                   If queue is full, set up a wait on it.
  845. *                                  Queue Not Full semaphore = Not Empty + 1.
  846.         bra   qempty               Go set up the Wait condition.
  847.  
  848. *******************************************************************************
  849. *                                                                             *
  850. * Resume a task. This is the opposite function of .suspnd.. The SUSPEND state *
  851. * is removed from the specified task. If the removal of the suspension makes  *
  852. * the task runnable, it is scheduled. If it is of higher priority than the    *
  853. * resuming task, a context switch will occur. Note that the task to be        *
  854. * resumed must be explicitly specified by the content of ACCA. A task may not *
  855. * resume itself, obviously.                                                   *
  856. *                                                                             *
  857. * calling sequence:                                                           *
  858. *                                                                             *
  859. *                   ACCA = task number                                        *
  860. *                   swi                                                       *
  861. *                    FCB   .resume.                                                 *
  862. *                   return:  All registers unchanged                          *
  863. *                                                                             *
  864. *******************************************************************************
  865.  
  866. .resume ldaa   #-_SUSPND-1
  867.         jmp    clrstat             Go clear the task's suspend status.
  868.  
  869. *******************************************************************************
  870. *                                                                             *
  871. * Suspend a task. This function is used to set a task into the SUSPEND state. *
  872. * Once in this state, it cannot be made runnable again except by a .resume.   *
  873. * ESR or an .execute. ESR. The task to be suspended is specified by putting   *
  874. * its task number in ACCA. If the current task is suspendeing itself, ACCA    *
  875. * is set to either the task number, if known, or more simply, a 0.            *
  876. *                                                                             *
  877. * calling sequence:                                                           *
  878. *                                                                             *
  879. *                   ACCA = task number (Otherwise this must be 0)             *
  880. *                   swi                                                       *
  881. *                    FCB   .suspend.                                                 *
  882. *                   return:  All registers unchanged                          *
  883. *                                                                             *
  884. *******************************************************************************
  885. .suspnd tstb
  886.         bne    notcur              See if the task to suspend is SELF.
  887.         ldx    curtcb              It is. Load TCB address.
  888.         bra    setspnd
  889. notcur  ldaa   #TCBLEN             If not current task, compute TCB address
  890.         mul                          of the specified task.
  891.         addd   #STATLS-TCBLEN
  892.         xgdx
  893. setspnd bset   STATE,x _SUSPND     Set the SUSPEND state in task's status byte.
  894.         jmp    endtsk
  895.  
  896. *******************************************************************************
  897. *                                                                             *
  898. * Terminate a task. This is a seldom used task but is included for complete-  *
  899. * ness. This function is called when it is desireable to terminate a task. It *
  900. * may be any task in the system including the caller. If the caller is going  *
  901. * to commit suicide, the given task number is set to 0. As a result of this   *
  902. * ESR, all timers which are in process for the terminated task are purged     *
  903. * the active timer list. The terminated task is set to an IDLE state. Once    *
  904. * terminated, a task may be restarted only by the .xeqt. command.             *
  905. *                                                                             *
  906. * calling sequence:                                                           *
  907. *                                                                             *
  908. *                   ACCA = Task number (0 if self)                            *
  909. *                   swi                                                       *
  910. *                    FCB  .terminate.                                                 *
  911. *                   return only if not terminating self. Registers unchanged  *
  912. *                                                                             *
  913. *******************************************************************************
  914.  
  915. .termn8 tstb                       Test for self.
  916.         bne    selfnot             Branch if task to terminate is not self.
  917.         ldab   curtsk              If self, get current task number.
  918.         ldx    curtcb              Then set up current task's TCB address.
  919.         stab   temp+1              Store in lower half of temp
  920.         bra    setidle             Then go set up the IDLE state.
  921. selfnot stab   temp+1              Store task # in lower half of temp.
  922.         ldaa   #TCBLEN
  923.         mul                        Compute TCB address of task to be terminated
  924.         addd   #STATLS-TCBLEN
  925.         xgdx
  926. setidle bset   STATE,x _IDLE       Set task's state to IDLE
  927.         clra
  928.         jmp    beginp              Then go purge all the task's timers.
  929.  
  930.  
  931. *******************************************************************************
  932. *                                                                             *
  933. * Execute a task. This function is used to put a task into the state of being *
  934. * ready to run. Its TCB is initialized so that its stack pointer is set to    *
  935. * the base address assigned to it. A stack frame is allocated on the task's   *
  936. * stack for an initial context. That context is set to contain the task's     *
  937. * starting address as the PC, while CCR, ACCB, and ACCA are cleared.          *
  938. *                                                                             *
  939. * calling sequence:                                                           *
  940. *                                                                             *
  941. *                   ACCA = task number (cannot be SELF)                       *
  942. *                   swi                                                       *
  943. *                    FCB   .execute.                                                   *
  944. *                   return:  All registers unchanged                          *
  945. *                                                                             *
  946. *******************************************************************************
  947.  
  948. .xeqt   pshb                       Save the task number
  949.         ldaa   #TCBDATL            Load length of TCB init data block
  950.         mul                        Multiply it by task number.
  951.         addd   #TCBDATA-TCBDATL
  952.         xgdx
  953.         ldd    RSTSP,x             Get the base address of task's stack space.
  954.         subd   #9
  955.         xgdy
  956.         ldd    STRTADR,x           Set up PC to be the starting address
  957.         ldx    TCBADDR,x
  958.         std    PC+1,y
  959.         clra
  960.         clrb
  961.         staa   CCR+1,y             Clear CCR
  962.         std    ACCB+1,y            Clear ACCB and ACCA
  963.         xgdy                       Put address of stack context back into D
  964.         std    ACTSP,x             Store address of the context in the TCB.
  965.         clra
  966.         jmp    clrbits             Go make the task runnable.
  967.  
  968. *******************************************************************************
  969. *                                                                             *
  970. * Delay a task for a period of time. This function uses the .timer. ESR       *
  971. * to set up a timer and a wait state on the specified task.  When the timer   *
  972. * elapses, the specified task is resumed.                                     *
  973. *                                                                             *
  974. * calling sequence:                                                           *
  975. *                                                                             *
  976. *                   ACCA = Task number (0 if self)                            *
  977. *                   ACCB = Semaphore number (0 if task's semaphore)           *
  978. *                   IX = Number of clock tocks to delay                       *
  979. *                   swi                                                       *
  980. *                    FCB   .delay.                                                  *
  981. *                   return. Registers unchanged                               *
  982. *                                                                             *
  983. *******************************************************************************
  984.  
  985. .delay  ldaa   #$FF                Set up switch.
  986.  
  987. *******************************************************************************
  988. *                                                                             *
  989. * Set up a timer for a task. This function is used to set a timer active for  *
  990. * the specified task. The timer is in clock tock units. A clock tock is a     *
  991. * system defined parameter. The timers are 16-bit timers; therefore, the      *
  992. * maximum duration of a timer is determined by the clock tock frequency.      *
  993. *                                                                             *
  994. * calling sequence:                                                           *
  995. *                                                                             *
  996. *                   ACCA = Task number (0 if self)                            *
  997. *                   ACCB = Semaphore number (0 if task's semaphore)           *
  998. *                   IX = Number of clock tocks in timer                       *
  999. *                   swi                                                       *
  1000. *                    FCB   .timer.                                                  *
  1001. *                   return. Registers unchanged                               *
  1002. *                           ACCA will be current task number if ACCA was = 0  *
  1003. *                             at the time of the ESR.                         *
  1004. *                                                                             *
  1005. *******************************************************************************
  1006.  
  1007. .timer  inca                       Set up switch: 0 = .delay., >0 = .timer.
  1008.         psha                       Save the switch.
  1009.         tstb                       Test for task being self.
  1010.         bne    notself             branch if task # is not 0.
  1011.         ldx    curtcb              Get address of current task's TCB
  1012.         ldaa   curtsk              get the current task number.
  1013.         staa   ACCA,y              Save it in the stack context.
  1014.         bra    gotask
  1015. notself ldaa   #TCBLEN
  1016.         mul                        Compute TCB address of specified task.
  1017.         addd   #STATLS-TCBLEN
  1018.         xgdx
  1019. gotask  ldab   ACCA,y              Get the actual task number
  1020.         pshb                       Save the task number.
  1021.         pshx                       Save the TCB address of the specified task.
  1022.         ldaa   ACCB,y              Get the semaphore number to use with timer.
  1023.         bne    gotsema             Branch if semaphore number is defined.
  1024.         ldaa   ACCA,y              If semaphore # = 0, Use task semaphore of
  1025.         adda   #NNAMSEM
  1026. gotsema psha                         specified task. Save semaphore number.
  1027.         ldd    IX,y                Get the timer value.
  1028.         bmi    savrset             If timer < 0, go save the timer reset value.
  1029.         clra
  1030.         clrb                       Timer is a one-shot. Reset time = 0.
  1031. savrset pshb                       Save the reset time.
  1032.         psha
  1033.         ldd    IX,y                Get the timer (as specified) again.
  1034.         bpl    savtime             See if it is cyclic or one-shot.
  1035.         coma                       If cyclic, be sure it is positive.
  1036.         negb
  1037.         bcs    savtime
  1038.         inca
  1039. savtime ldx    #ACTIVE             Let IX contain the address of the active
  1040.         sei                          timers.
  1041. tloop   ldy    CLINK,x             Get address of next timer.
  1042.         beq    append              If end-of-list, go append new timer.
  1043.         cpd    CTOCKS,y            There is a timer that is active. Compare its
  1044. *                                    timer counts with those in the new timer.
  1045.         blt    nsrtm               If there are fewer tocks in the new timer,
  1046. *                                    go insert the new timer in front of the
  1047. *                                    active one.
  1048.         subd   CTOCKS,y            If not, subtract the tocks of the active
  1049. *                                    timer from the new timer.
  1050.         ldx    CLINK,x             Then move down the thread.
  1051.         bra    tloop
  1052. nsrtm   pshb                       Save the remaining time in the timer.
  1053.         psha
  1054.         subd   CTOCKS,y            Update the active timer in the list
  1055.         coma                         subtracting the new timer's residual
  1056.         negb                         from it.
  1057.         bcs    updt
  1058.         inca
  1059. updt    std    CTOCKS,y            Store the updated timer.
  1060.         bra    linkit
  1061. append  pshb                       Save the timer.
  1062.         psha
  1063. linkit  ldd    CLINK,x             Get the address of the current active timer.
  1064.         pshb
  1065.         psha                       Save the address
  1066.         ldd    FREE                Get the address of the next free timer block
  1067.         std    CLINK,x             Store it in link of the active timer block.
  1068.         xgdy                       IY now contains address of the new timer blk
  1069.         ldd    CLINK,y             Get address of next free timer block.
  1070.         std    FREE                Save it in pointer.
  1071.         pula
  1072.         pulb                       Get address of the current active timer.
  1073.         std    CLINK,y             Store it in link word of the new timer block
  1074.         pula
  1075.         pulb
  1076.         std    CTOCKS,y            Store timer tocks in new timer block.
  1077.         pula
  1078.         pulb
  1079.         std    CRESET,y            Store timer reset value.
  1080.         pulb
  1081.         stab   CSEMA,y             Store semaphore #
  1082.         pulx                       Get TCB address
  1083.         pula
  1084.         staa   CTASK,y             Store # of the task using the timer.
  1085.         pula                       Get the switch
  1086.         tsta
  1087.         beq    dlay                Branch if switch set for .delay.
  1088.         jmp    dopend              ** (V1.1 #3) **
  1089. dlay    bset   STATE,x _WAIT       If .delay., set wait state in task.
  1090.         ldx    #FLGTBL-1
  1091.         abx                        Compute address of semaphore.
  1092.         ldaa   CTASK,y             Get the # of the task being delayed.
  1093.         nega
  1094.         staa   0,x                 Set the semaphore to WAIT state.
  1095.         jmp    endtsk
  1096.  
  1097. *******************************************************************************
  1098. *                                                                             *
  1099. * Purge a timer or timers. This function is used to purge one timer for a     *
  1100. * given task and semaphore combination or all of the timers for a given task. *
  1101. * Each timer so purged has its associated semaphore reset to the PENDing      *
  1102. * state.                                                                      *
  1103. *                                                                             *
  1104. * calling sequence:                                                           *
  1105. *                                                                             *
  1106. *                   ACCA = Task number (0 if self)                            *
  1107. *                   ACCB = Semaphore number (0 if task's semaphore)           *
  1108. *                   IX0-7= Switch: 0 = purge all timers for given task        *
  1109. *                                  >0 = purge timer matching task and sema    *
  1110. *                   swi                                                       *
  1111. *                    FCB   .purge.                                                  *
  1112. *                   return. Registers unchanged                               *
  1113. *                                                                             *
  1114. *******************************************************************************
  1115.  
  1116. .purge  tstb
  1117.         bne    gotsk               If ACCB > 0, task is specific.
  1118.         ldab   curtsk              Otherwise, use current task.
  1119. gotsk   std    temp                Save task and semaphore numbers.
  1120. chksema clra
  1121.         tst    IXL,y               Test the switch for type of purge.
  1122.         beq    beginp              Branch if switch = 0.
  1123.         ldaa   temp                Otherwise get the semaphore number to match.
  1124.         bne    beginp              Branch if it is defined.
  1125.         ldaa   temp+1              Otherwise use the task semaphore.
  1126.         adda   #NNAMSEM
  1127. beginp  ldab   temp+1              Load the task number to be matched.
  1128.         ldx    #ACTIVE             Set up pointer to 1st active timer.
  1129.         sei                        Interrupts off during this next phase.
  1130. ploop   ldy    CLINK,x             Set IY = address of next active timer.
  1131.         beq    endpurg             If end-of-list, purge is complete.
  1132.         cmpb   CTASK,y             Compare task # in active timer to object #.
  1133.         beq    sametsk             Branch if both task numbers are equal.
  1134. chknxt  ldx    CLINK,x             If not equal, walk list to next active timer
  1135.         bra    ploop               Keep looking.
  1136. sametsk tsta                       Tasks are equal. Test if semaphore # = 0.
  1137.         beq    remove              Branch if semaphore # = 0.
  1138.         cmpa   CSEMA,y             If semaphore > 0, Check it against semaphore
  1139.         bne    chknxt                in active timer. Branch if not the same.
  1140. remove  pshb
  1141.         psha                       Save semaphore and task number.
  1142.         pshx                       Save IX for a moment.
  1143.         ldx    #FLGTBL-1
  1144.         ldab   CSEMA,y
  1145.         abx                        Compute address of timer's semaphore.
  1146.         ldaa   #_PEND
  1147.         staa   0,x                 Set timer's semaphore back to PENDing.
  1148.         pulx                       Restore IX
  1149.         ldd    CLINK,y             Get the pointer to next active timer block.
  1150.         std    CLINK,x             Store it in link word of pointer to current
  1151. *                                    active timer.
  1152. *** (V1.4 #3) *** |
  1153.         beq    prgexit                                                      |
  1154.         ldd    CTOCKS,y            Update the next timer in link            |
  1155. *** (V1.5 #1) *** +
  1156.         pshy                       Save y                                 +
  1157.         ldy    CLINK,y                                                      |
  1158.         addd   CTOCKS,y                                                     |
  1159.         std    CTOCKS,y                                                     |
  1160.         puly                       Restore Y                              +
  1161. prgexit ldd    FREE                                                         |
  1162.         std    CLINK,y             Make current timer block the first free one.
  1163.         xgdy
  1164.         std    FREE
  1165.         pula
  1166.         pulb                       Restore task and semaphore numbers.
  1167.         tsta                       Test the semaphore number.
  1168.         beq    ploop               Branch if semaphore = 0. (purge all timers)
  1169. endpurg jmp    endtsk              Otherwise that's all.
  1170.  
  1171.